package controllers;

import java.util.Calendar;
import java.util.Collection;
import java.util.GregorianCalendar;

import person.Customer;
import person.Person;
import person.Customer.Type;
import transaction.Transaction;
import budget.Budget;
import budget.Request;
import budget.Service;
import budget.Service.ServiceType;
import contract.EmployeeContract;
import contract.EventContract;
import controllers.dbWrapper.EventWrapper;
import controllers.dbWrapper.UserWrapper;
import event.Event;

public class Checks {
	private static boolean disponibilityValutation(Calendar date) throws Exception{
		//other condition on public holiday
		return EventWrapper.isAvailable(date, false);
	}
	/*Checks Rivisti*/
	
	//Check per l'Utente
		public static void checkUser(String userName, String password, boolean checkExist) throws Exception{
			Checks.checkUserName(userName,checkExist);
			Checks.checkPassword(password);
		}
		public static void checkUserName(String userName,boolean checkExist)throws Exception{
			/*Controllo formattazione*/
			if(userName.length()<3||userName.length()>16) throw new Exception(FieldErrors.USER_NAME_INVALID.name());
			/*Controllo esistenza*/
			if(checkExist && UserWrapper.exists(userName, false)){
				throw new Exception(FieldErrors.USER_NAME_EXISTS.name());
			}
		}
		public static void checkPassword(String password)throws Exception{
			/*Controllo formattazione*/
			if(password.length()<4||password.length()>16) throw new Exception(FieldErrors.PASSWORD_INVALID.name());
			if(!password.matches("[A-Za-z0-9]{4,32}")) throw new Exception(FieldErrors.PASSWORD_INVALID.name());
		}
		public static void checkTransaction(Transaction transaction)throws Exception{
			/*Controllo della lunghezza della */
			if(transaction.getReasonForPayment().length() > Checks.MEMO_MAX_LEN) throw new Exception(FieldErrors.TOO_LONG_CLAUSES_FIELD.name());
			/*Controllo della somma*/
			if(!isLegalAmount(transaction.getAmount()))throw new Exception(FieldErrors.TRANSACTION_INVALID_AMOUNT.name());
			/*Controllo del creditore o beneficiario*/
			Checks.checkPerson(transaction.getBeneficiary());
			/*Controllo transazioni postdatate*/
			if(isAfterToday(transaction.getDate()))throw new Exception(FieldErrors.TRANSACTION_INVALID_DATE.name());;
		}
		
		// Checks per Employee Contract
		public static void checkEmployeeContract(EmployeeContract empCont) throws Exception{
			Checks.checkPerson(empCont.getPerson());
			if(empCont.getClauses().length() > Checks.MEMO_MAX_LEN) throw new Exception(FieldErrors.TOO_LONG_CLAUSES_FIELD.name());
			if(isAfterToday(empCont.getStipulationDate())) throw new Exception(FieldErrors.STIPULATION_DATE_ERROR.name());
			if(isBeforeToday(empCont.getValidityDate())) throw new Exception(FieldErrors.VALIDITY_DATE_ERROR.name());
			if(!isLegalAmount(empCont.getWages())) throw new Exception(FieldErrors.INVALID_WAGES_AMOUNT.name());
		}
		
		/*Utility private*/
		private static boolean isPhoneOrFaxNumber(String number){
			return number.matches("[0-9]*");
		}		
		private static boolean isBeforeToday(Calendar date){
			GregorianCalendar today=new GregorianCalendar();
			today.set(Calendar.HOUR_OF_DAY,0);
			today.set(Calendar.MINUTE,1);
			return date.before(today);
		}
		private static boolean isLegalAmount(Double amount){
			return amount.toString().matches("[0-9]*\\.[0-9]{0,2}0*")&&amount>0;
		}
		private static boolean isAfterToday(Calendar date){
			GregorianCalendar today=new GregorianCalendar();
			today.set(Calendar.HOUR_OF_DAY,23);
			today.set(Calendar.MINUTE,59);
			return date.after(today);
			
		}
	/*Fine check rivisti*/

	// Checks per la Richiesta di preventivo
	public static void checkRequest(Request r)throws Exception{
		if (r.getEvent()==null)throw new Exception(ExceptionsTypes.DIPENDENCE_ERROR.name());
		if (r.getCustomer()==null)throw new Exception(ExceptionsTypes.DIPENDENCE_ERROR.name());
		Checks.checkCustomer(r.getCustomer());
		Checks.checkEvent(r.getEvent());
		if(r.getMemo().length() >= 300) throw new Exception(FieldErrors.TOO_LONG_REQUEST_MEMO.name());
	}
	// Check per l'Evento
	public static void checkEvent (Event e) throws Exception{
		Calendar realDate = new GregorianCalendar(); 
		realDate.add(GregorianCalendar.DAY_OF_MONTH, 3); //tempo tecnico per realizzare un evento
		//Controllo data di inizio [evento]
		if(e.getBeginDate().before(realDate))throw new Exception(FieldErrors.BEGIN_DATE_INVALID.name());			
		//Controllo data di fine [evento]
		if(e.getBeginDate().after(e.getEndDate()))throw new Exception(FieldErrors.END_DATE_INVALID.name());	
		//Controllo disponibilit�
		for(int i = 0; i< e.getDays(); i++){
			GregorianCalendar cal = new GregorianCalendar();
			cal.setTime(e.getBeginDate().getTime());
			cal.add(Calendar.DAY_OF_MONTH, i);
			if(!disponibilityValutation(cal))
				throw new Exception(ExceptionsTypes.UNAVAILABLE_DATE.name());
		}
		//Controllo nome [evento]
		if(e.getEventName().trim().equals("")|| e.getEventName().length()>30 )throw new Exception(FieldErrors.EVENT_NAME_INVALID.name());			
		//Controllo luogo [evento]
		if(e.getPlace().trim().equals("")|| e.getPlace().length()>60)throw new Exception(FieldErrors.PLACE_INVALID.name());			
		//Controllo numero invitati	[Evento]
		if(e.getGuestsNumber()<1 )throw new Exception(FieldErrors.GUESTS_NUMBER_INVALID.name());
	}
	
	//Check per il Cliente
	public static void checkCustomer(Customer c)throws Exception{
		Checks.checkPerson(c);
		//Controllo partita iva
		if(c.getType()==Type.COMPANY){
			try{Long.parseLong(c.getVAT());}
			catch (NumberFormatException e2) {throw new Exception(FieldErrors.VAT_INVALID.name());}
		}else{c.setVAT("");}
	}
	
	//Check per la Persona
	public static void checkPerson(Person p) throws Exception{	
		Calendar actualDate = new GregorianCalendar();
		Calendar trueDate =new GregorianCalendar();
		trueDate.add(Calendar.YEAR, -18);
		actualDate.setTimeInMillis(actualDate.getTimeInMillis()-60000);
		//Controllo data di nascita [persona]
		if(p.getBirthDate().after(trueDate))throw new Exception(FieldErrors.BIRTHDAY_INVALID.name());
		//Controllo nome [persona]
		if(p.getName().trim().equals("")||p.getName().length()>30)throw new Exception(FieldErrors.NAME_INVALID.name());
		//Controllo cognome [persona]
		if(p.getSurname().trim().equals("")|| p.getSurname().length()>30) throw new Exception(FieldErrors.SURNAME_INVALID.name());
		//Controllo codice fiscale [persona]
		if(!p.getTaxCode().toUpperCase().matches("[A-Z]{6}[0-9]{2}[A-Z][0-9]{2}[A-Z0-9]{5}"))throw new Exception(FieldErrors.TAX_INVALID.name());	
		//Controllo recapito [persona]
		if(p.getAddress().trim().equals("") && p.getEmail().trim().equals("") && p.getFax().trim().equals("") && p.getPhoneNumber().trim().equals(""))throw new Exception(FieldErrors.ADDRESS_ERROR.name());
		//Controllo indirizzo [persona]
		if(p.getAddress().length()>60) throw new Exception(FieldErrors.ADDRESS_INVALID.name());	
		//controllo numero di telefono
		if(!isPhoneOrFaxNumber(p.getPhoneNumber()))throw new Exception(FieldErrors.PHONE_INVALID.name());		
		//Controllo fax
		if(!p.getFax().equals(""))
			if(!isPhoneOrFaxNumber(p.getFax()))throw new Exception(FieldErrors.FAX_INVALID.name());		
		//Controllo email
		String[] chiocciola = p.getEmail().split("@");
		if(chiocciola.length!=2)throw new Exception(FieldErrors.EMAIL_INDVALID.name());
		if(!chiocciola[1].contains("."))throw new Exception(FieldErrors.EMAIL_INDVALID.name());
		if(p.getBirthDate().after(actualDate)) throw new Exception(FieldErrors.BIRTHDAY_INVALID.name());
	}
	//Check per l'Employee
	public static void checkEmployee(EmployeeContract e) throws Exception{
		Checks.checkPerson(e.getPerson());
	}
	
	//Check del Budget
	public static void checkBudget(Budget b) throws Exception{
		if (b.getRequest()==null)throw new Exception(ExceptionsTypes.DIPENDENCE_ERROR.name());
		
		if(b.getTotalCostWhitoutAdjustment()<0) throw new Exception(FieldErrors.TOTAL_COST_WITHOUT_ADJUSTMENT.name());
		if(b.getAdjustments()<-b.getTotalCostWhitoutAdjustment()) throw new Exception(FieldErrors.ADJUSTMENT_INVALID.name());
		if(b.getLastDate().before(new GregorianCalendar())) throw new Exception(FieldErrors.LAST_DATE_INVALID.name());
		Collection<Service> services = b.getServices();
		for(Service serv: services){
			if (serv.isOnlyOne()){
				if(serv.getServiceType()==ServiceType.OTHER){
					for(Service srv:services){
						if(serv.getName().equalsIgnoreCase(srv.getName()) && serv.getId() != srv.getId())
							throw new Exception(FieldErrors.LIST_OF_SERVICES_INVALID.name());
					}
				}else{
					for(Service srv:services){
						if(serv.getServiceType()==srv.getServiceType() && serv.getId() != srv.getId())
							throw new Exception(FieldErrors.LIST_OF_SERVICES_INVALID.name());
					}
				}
			}else
				if(serv.getServiceType()!=ServiceType.OTHER){
					for(Service srv:services){
						if(serv.getName().equalsIgnoreCase(srv.getName())&& serv.getId() != srv.getId())
							throw new Exception(FieldErrors.LIST_OF_SERVICES_INVALID.name());
					}
				}
		}
	}
	//Check per il Contratto Evento
	public static void checkEventContract(EventContract evnCont) throws Exception{
		Calendar actualDate = new GregorianCalendar();
		actualDate.setTimeInMillis(actualDate.getTimeInMillis()-60000);
		//Check della presenza del budget
		if(evnCont.getBudget()==null) throw new Exception(ExceptionsTypes.DIPENDENCE_ERROR.name());
		//Check del campo clausula in modo che non sia troppo lungo
		if(evnCont.getClauses().length() > 300) throw new Exception(FieldErrors.TOO_LONG_CLAUSES_FIELD.name());
		//Check del campo Data di Stipulazione del contratto
		//if(evnCont.getStipulationDate().before(actualDate)) throw new Exception(FieldErrors.STIPULATION_DATE_ERROR.name());
		//Check del campo Data di Validit� del contratto
		//if(evnCont.getValidityDate().before(actualDate)) throw new Exception(FieldErrors.VALIDITY_DATE_ERROR.name());
	}
	private static final int MEMO_MAX_LEN=300;
}
